home *** CD-ROM | disk | FTP | other *** search
/ Computer Shopper 242 / Issue 242 - April 2008 - DPCS0408DVD.ISO / Open Source / AutoHotKey / Source / AutoHotkey104705_source.exe / source / clipboard.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-08-17  |  22.8 KB  |  474 lines

  1. /*
  2. AutoHotkey
  3.  
  4. Copyright 2003-2007 Chris Mallett (support@autohotkey.com)
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15. */
  16.  
  17. #include "stdafx.h" // pre-compiled headers
  18. #include "clipboard.h"
  19. #include "globaldata.h"  // for g_script.ScriptError() and g_ClipboardTimeout
  20. #include "application.h" // for MsgSleep()
  21. #include "util.h" // for strlcpy()
  22.  
  23. size_t Clipboard::Get(char *aBuf)
  24. // If aBuf is NULL, it returns the length of the text on the clipboard and leaves the
  25. // clipboard open.  Otherwise, it copies the clipboard text into aBuf and closes
  26. // the clipboard (UPDATE: But only if the clipboard is still open from a prior call
  27. // to determine the length -- see later comments for details).  In both cases, the
  28. // length of the clipboard text is returned (or the value CLIPBOARD_FAILURE if error).
  29. // If the clipboard is still open when the next MsgSleep() is called -- presumably
  30. // because the caller never followed up with a second call to this function, perhaps
  31. // due to having insufficient memory -- MsgSleep() will close it so that our
  32. // app doesn't keep the clipboard tied up.  Note: In all current cases, the caller
  33. // will use MsgBox to display an error, which in turn calls MsgSleep(), which will
  34. // immediately close the clipboard.
  35. {
  36.     // Seems best to always have done this even if we return early due to failure:
  37.     if (aBuf)
  38.         // It should be safe to do this even at its peak capacity, because caller
  39.         // would have then given us the last char in the buffer, which is already
  40.         // a zero terminator, so this would have no effect:
  41.         *aBuf = '\0';
  42.  
  43.     UINT i, file_count = 0;
  44.     BOOL clipboard_contains_text = IsClipboardFormatAvailable(CF_TEXT);
  45.     BOOL clipboard_contains_files = IsClipboardFormatAvailable(CF_HDROP);
  46.     if (!(clipboard_contains_text || clipboard_contains_files))
  47.         return 0;
  48.  
  49.     if (!mIsOpen)
  50.     {
  51.         // As a precaution, don't give the caller anything from the clipboard
  52.         // if the clipboard isn't already open from the caller's previous
  53.         // call to determine the size of what's on the clipboard (no other app
  54.         // can alter its size while we have it open).  The is to prevent a
  55.         // buffer overflow from happening in a scenario such as the following:
  56.         // Caller calls us and we return zero size, either because there's no
  57.         // CF_TEXT on the clipboard orthere was a problem opening the clipboard.
  58.         // In these two cases, the clipboard isn't open, so by the time the
  59.         // caller calls us again, there's a chance (vanishingly small perhaps)
  60.         // that another app (if our thread were preempted long enough, or the
  61.         // platform is multiprocessor) will have changed the contents of the
  62.         // clipboard to something larger than zero.  Thus, if we copy that
  63.         // into the caller's buffer, the buffer might overflow:
  64.         if (aBuf)
  65.             return 0;
  66.         if (!Open())
  67.         {
  68.             // Since this should be very rare, a shorter message is now used.  Formerly, it was
  69.             // "Could not open clipboard for reading after many timed attempts. Another program is probably holding it open."
  70.             Close(CANT_OPEN_CLIPBOARD_READ);
  71.             return CLIPBOARD_FAILURE;
  72.         }
  73.         if (   !(mClipMemNow = g_clip.GetClipboardDataTimeout(clipboard_contains_files ? CF_HDROP : CF_TEXT))   )
  74.         {
  75.             // v1.0.47.04: Commented out the following that had been in effect when clipboard_contains_files==false:
  76.             //    Close("GetClipboardData"); // Short error message since so rare.
  77.             //    return CLIPBOARD_FAILURE;
  78.             // This was done because there are situations when GetClipboardData can fail indefinitely.
  79.             // For example, in Firefox, pulling down the Bookmarks menu then right-clicking "Bookmarks Toolbar
  80.             // Folder" then selecting "Copy" puts one or more formats on the clipboard that cause this problem.
  81.             // For details, search the forum for TYMED_NULL.
  82.             //
  83.             // v1.0.42.03: For the fix below, GetClipboardDataTimeout() knows not to try more than once
  84.             // for CF_HDROP.
  85.             // Fix for v1.0.31.02: When clipboard_contains_files==true, tolerate failure, which happens
  86.             // as a normal/expected outcome when there are files on the clipboard but either:
  87.             // 1) zero of them;
  88.             // 2) the CF_HDROP on the clipboard is somehow misformatted.
  89.             // If you select the parent ".." folder in WinRar then use the following hotkey, the script
  90.             // would previously yield a runtime error:
  91.             //#q::
  92.             //Send, ^c
  93.             //ClipWait, 0.5, 1
  94.             //msgbox %Clipboard%
  95.             //Return
  96.             Close();
  97.             if (aBuf)
  98.                 *aBuf = '\0';
  99.             return 0;
  100.         }
  101.         // Although GlobalSize(mClipMemNow) can yield zero in some cases -- in which case GlobalLock() should
  102.         // not be attempted -- it probably can't yield zero for CF_HDROP and CF_TEXT because such a thing has
  103.         // never been reported by anyone.  Therefore, GlobalSize() is currently not called.
  104.         if (   !(mClipMemNowLocked = (char *)GlobalLock(mClipMemNow))   )
  105.         {
  106.             Close("GlobalLock");  // Short error message since so rare.
  107.             return CLIPBOARD_FAILURE;
  108.         }
  109.         // Otherwise: Update length after every successful new open&lock:
  110.         // Determine the length (size - 1) of the buffer than would be
  111.         // needed to hold what's on the clipboard:
  112.         if (clipboard_contains_files)
  113.         {
  114.             if (file_count = DragQueryFile((HDROP)mClipMemNowLocked, 0xFFFFFFFF, "", 0))
  115.             {
  116.                 mLength = (file_count - 1) * 2;  // Init; -1 if don't want a newline after last file.
  117.                 for (i = 0; i < file_count; ++i)
  118.                     mLength += DragQueryFile((HDROP)mClipMemNowLocked, i, NULL, 0);
  119.             }
  120.             else
  121.                 mLength = 0;
  122.         }
  123.         else // clipboard_contains_text
  124.             mLength = strlen(mClipMemNowLocked);
  125.         if (mLength >= CLIPBOARD_FAILURE) // Can't realistically happen, so just indicate silent failure.
  126.             return CLIPBOARD_FAILURE;
  127.     }
  128.     if (!aBuf)
  129.         return mLength;
  130.         // Above: Just return the length; don't close the clipboard because we expect
  131.         // to be called again soon.  If for some reason we aren't called, MsgSleep()
  132.         // will automatically close the clipboard and clean up things.  It's done this
  133.         // way to avoid the chance that the clipboard contents (and thus its length)
  134.         // will change while we don't have it open, possibly resulting in a buffer
  135.         // overflow.  In addition, this approach performs better because it avoids
  136.         // the overhead of having to close and reopen the clipboard.
  137.  
  138.     // Otherwise:
  139.     if (clipboard_contains_files)
  140.     {
  141.         if (file_count = DragQueryFile((HDROP)mClipMemNowLocked, 0xFFFFFFFF, "", 0))
  142.             for (i = 0; i < file_count; ++i)
  143.             {
  144.                 // Caller has already ensured aBuf is large enough to hold them all:
  145.                 aBuf += DragQueryFile((HDROP)mClipMemNowLocked, i, aBuf, 999);
  146.                 if (i < file_count - 1) // i.e. don't add newline after the last filename.
  147.                 {
  148.                     *aBuf++ = '\r';  // These two are the proper newline sequence that the OS prefers.
  149.                     *aBuf++ = '\n';
  150.                 }
  151.                 //else DragQueryFile() has ensured that aBuf is terminated.
  152.             }
  153.         // else aBuf has already been terminated upon entrance to this function.
  154.     }
  155.     else
  156.         strcpy(aBuf, mClipMemNowLocked);  // Caller has already ensured that aBuf is large enough.
  157.     // Fix for v1.0.37: Close() is no longer called here because it prevents the clipboard variable
  158.     // from being referred to more than once in a line.  For example:
  159.     // Msgbox %Clipboard%%Clipboard%
  160.     // ToolTip % StrLen(Clipboard) . Clipboard
  161.     // Instead, the clipboard is later closed in other places (search on CLOSE_CLIPBOARD_IF_OPEN
  162.     // to find them).  The alternative to fixing it this way would be to let it reopen the clipboard
  163.     // by means getting rid of the following lines above:
  164.     //if (aBuf)
  165.     //    return 0;
  166.     // However, that has the risks described in the comments above those two lines.
  167.     return mLength;
  168. }
  169.  
  170.  
  171.  
  172. ResultType Clipboard::Set(char *aBuf, UINT aLength) //, bool aTrimIt)
  173. // Returns OK or FAIL.
  174. {
  175.     // It was already open for writing from a prior call.  Return failure because callers that do this
  176.     // are probably handling things wrong:
  177.     if (IsReadyForWrite()) return FAIL;
  178.  
  179.     if (!aBuf)
  180.     {
  181.         aBuf = "";
  182.         aLength = 0;
  183.     }
  184.     else
  185.         if (aLength == UINT_MAX) // Caller wants us to determine the length.
  186.             aLength = (UINT)strlen(aBuf);
  187.  
  188.     if (aLength)
  189.     {
  190.         if (!PrepareForWrite(aLength + 1))
  191.             return FAIL;  // It already displayed the error.
  192.         strlcpy(mClipMemNewLocked, aBuf, aLength + 1);  // Copy only a substring, if aLength specifies such.
  193.         // Only trim when the caller told us to, rather than always if g_script.mIsAutoIt2
  194.         // is true, since AutoIt2 doesn't always trim things (e.g. FileReadLine probably
  195.         // does not trim the line that was read into its output var).  UPDATE: This is
  196.         // no longer needed because I think AutoIt2 only auto-trims when SetEnv is used:
  197.         //if (aTrimIt)
  198.         //    trim(mClipMemNewLocked);
  199.     }
  200.     // else just do the below to empty the clipboard, which is different than setting
  201.     // the clipboard equal to the empty string: it's not truly empty then, as reported
  202.     // by IsClipboardFormatAvailable(CF_TEXT) -- and we want to be able to make it truly
  203.     // empty for use with functions such as ClipWait:
  204.     return Commit();  // It will display any errors.
  205. }
  206.  
  207.  
  208.  
  209. char *Clipboard::PrepareForWrite(size_t aAllocSize)
  210. {
  211.     if (!aAllocSize) return NULL; // Caller should ensure that size is at least 1, i.e. room for the zero terminator.
  212.     if (IsReadyForWrite())
  213.         // It was already prepared due to a prior call.  Currently, the most useful thing to do
  214.         // here is return the memory area that's already been reserved:
  215.         return mClipMemNewLocked;
  216.     // Note: I think GMEM_DDESHARE is recommended in addition to the usual GMEM_MOVEABLE:
  217.     // UPDATE: MSDN: "The following values are obsolete, but are provided for compatibility
  218.     // with 16-bit Windows. They are ignored.": GMEM_DDESHARE
  219.     if (   !(mClipMemNew = GlobalAlloc(GMEM_MOVEABLE, aAllocSize))   )
  220.     {
  221.         g_script.ScriptError("GlobalAlloc");  // Short error message since so rare.
  222.         return NULL;
  223.     }
  224.     if (   !(mClipMemNewLocked = (char *)GlobalLock(mClipMemNew))   )
  225.     {
  226.         mClipMemNew = GlobalFree(mClipMemNew);  // This keeps mClipMemNew in sync with its state.
  227.         g_script.ScriptError("GlobalLock"); // Short error message since so rare.
  228.         return NULL;
  229.     }
  230.     mCapacity = (UINT)aAllocSize; // Keep mCapacity in sync with the state of mClipMemNewLocked.
  231.     *mClipMemNewLocked = '\0'; // Init for caller.
  232.     return mClipMemNewLocked;  // The caller can now write to this mem.
  233. }
  234.  
  235.  
  236.  
  237. ResultType Clipboard::Commit(UINT aFormat)
  238. // If this is called while mClipMemNew is NULL, the clipboard will be set to be truly
  239. // empty, which is different from writing an empty string to it.  Note: If the clipboard
  240. // was already physically open, this function will close it as part of the commit (since
  241. // whoever had it open before can't use the prior contents, since they're invalid).
  242. {
  243.     if (!mIsOpen && !Open())
  244.         // Since this should be very rare, a shorter message is now used.  Formerly, it was
  245.         // "Could not open clipboard for writing after many timed attempts.  Another program is probably holding it open."
  246.         return AbortWrite(CANT_OPEN_CLIPBOARD_WRITE);
  247.     if (!EmptyClipboard())
  248.     {
  249.         Close();
  250.         return AbortWrite("EmptyClipboard"); // Short error message since so rare.
  251.     }
  252.     if (mClipMemNew)
  253.     {
  254.         bool new_is_empty = false;
  255.         // Unlock prior to calling SetClipboardData:
  256.         if (mClipMemNewLocked) // probably always true if we're here.
  257.         {
  258.             // Best to access the memory while it's still locked, which is why this temp var is used:
  259.             // v1.0.40.02: The following was fixed to properly recognize 0x0000 as the Unicode string terminator,
  260.             // which fixes problems with Transform Unicode.
  261.             new_is_empty = !mClipMemNewLocked[0] && (aFormat != CF_UNICODETEXT || !mClipMemNewLocked[1]);
  262.             GlobalUnlock(mClipMemNew); // mClipMemNew not mClipMemNewLocked.
  263.             mClipMemNewLocked = NULL;  // Keep this in sync with the above action.
  264.             mCapacity = 0; // Keep mCapacity in sync with the state of mClipMemNewLocked.
  265.         }
  266.         if (new_is_empty)
  267.             // Leave the clipboard truly empty rather than setting it to be the
  268.             // empty string (i.e. these two conditions are NOT the same).
  269.             // But be sure to free the memory since we're not giving custody
  270.             // of it to the system:
  271.             mClipMemNew = GlobalFree(mClipMemNew);
  272.         else
  273.             if (SetClipboardData(aFormat, mClipMemNew))
  274.                 // In any of the failure conditions above, Close() ensures that mClipMemNew is
  275.                 // freed if it was allocated.  But now that we're here, the memory should not be
  276.                 // freed because it is owned by the clipboard (it will free it at the appropriate time).
  277.                 // Thus, we relinquish the memory because we shouldn't be looking at it anymore:
  278.                 mClipMemNew = NULL;
  279.             else
  280.             {
  281.                 Close();
  282.                 return AbortWrite("SetClipboardData"); // Short error message since so rare.
  283.             }
  284.     }
  285.     // else we will close it after having done only the EmptyClipboard(), above.
  286.     // Note: Decided not to update mLength for performance reasons (in case clipboard is huge).
  287.     // Anyway, it seems rather pointless because once the clipboard is closed, our app instantly
  288.     // loses sight of how large it is, so the the value of mLength wouldn't be reliable unless
  289.     // the clipboard were going to be immediately opened again.
  290.     return Close();
  291. }
  292.  
  293.  
  294.  
  295. ResultType Clipboard::AbortWrite(char *aErrorMessage)
  296. // Always returns FAIL.
  297. {
  298.     // Since we were called in conjunction with an aborted attempt to Commit(), always
  299.     // ensure the clipboard is physically closed because even an attempt to Commit()
  300.     // should physically close it:
  301.     Close();
  302.     if (mClipMemNewLocked)
  303.     {
  304.         GlobalUnlock(mClipMemNew); // mClipMemNew not mClipMemNewLocked.
  305.         mClipMemNewLocked = NULL;
  306.         mCapacity = 0; // Keep mCapacity in sync with the state of mClipMemNewLocked.
  307.     }
  308.     // Above: Unlock prior to freeing below.
  309.     if (mClipMemNew)
  310.         mClipMemNew = GlobalFree(mClipMemNew);
  311.     // Caller needs us to always return FAIL:
  312.     return *aErrorMessage ? g_script.ScriptError(aErrorMessage) : FAIL;
  313. }
  314.  
  315.  
  316.  
  317. ResultType Clipboard::Close(char *aErrorMessage)
  318. // Returns OK or FAIL (but it only returns FAIL if caller gave us a non-NULL aErrorMessage).
  319. {
  320.     // Always close it ASAP so that it is free for other apps to use:
  321.     if (mIsOpen)
  322.     {
  323.         if (mClipMemNowLocked)
  324.         {
  325.             GlobalUnlock(mClipMemNow); // mClipMemNow not mClipMemNowLocked.
  326.             mClipMemNowLocked = NULL;  // Keep this in sync with its state, since it's used as an indicator.
  327.         }
  328.         // Above: It's probably best to unlock prior to closing the clipboard.
  329.         CloseClipboard();
  330.         mIsOpen = false;  // Even if above fails (realistically impossible?), seems best to do this.
  331.         // Must do this only after GlobalUnlock():
  332.         mClipMemNow = NULL;
  333.     }
  334.     // Do this cleanup for callers that didn't make it far enough to even open the clipboard.
  335.     // UPDATE: DO *NOT* do this because it is valid to have the clipboard in a "ReadyForWrite"
  336.     // state even after we physically close it.  Some callers rely on that.
  337.     //if (mClipMemNewLocked)
  338.     //{
  339.     //    GlobalUnlock(mClipMemNew); // mClipMemNew not mClipMemNewLocked.
  340.     //    mClipMemNewLocked = NULL;
  341.     //    mCapacity = 0; // Keep mCapacity in sync with the state of mClipMemNewLocked.
  342.     //}
  343.     //// Above: Unlock prior to freeing below.
  344.     //if (mClipMemNew)
  345.     //    // Commit() was never called after a call to PrepareForWrite(), so just free the memory:
  346.     //    mClipMemNew = GlobalFree(mClipMemNew);
  347.     if (aErrorMessage && *aErrorMessage)
  348.         // Caller needs us to always return FAIL if an error was displayed:
  349.         return g_script.ScriptError(aErrorMessage);
  350.  
  351.     // Seems best not to reset mLength.  But it will quickly become out of date once
  352.     // the clipboard has been closed and other apps can use it.
  353.     return OK;
  354. }
  355.  
  356.  
  357.  
  358. HANDLE Clipboard::GetClipboardDataTimeout(UINT uFormat)
  359. // Same as GetClipboardData() except that it doesn't give up if the first call to GetClipboardData() fails.
  360. // Instead, it continues to retry the operation for the number of milliseconds in g_ClipboardTimeout.
  361. // This is necessary because GetClipboardData() has been observed to fail in repeatable situations (this
  362. // is strange because our thread already has the clipboard locked open -- presumably it happens because the
  363. // GetClipboardData() is unable to start a data stream from the application that actually serves up the data).
  364. // If cases where the first call to GetClipboardData() fails, a subsequent call will often succeed if you give
  365. // the owning application (such as Excel and Word) a little time to catch up.  This is especially necessary in
  366. // the OnClipboardChange label, where sometimes a clipboard-change notification comes in before the owning
  367. // app has finished preparing its data for subsequent readers of the clipboard.
  368. {
  369. #ifdef DEBUG_BY_LOGGING_CLIPBOARD_FORMATS  // Provides a convenient log of clipboard formats for analysis.
  370.     static FILE *fp = fopen("c:\\debug_clipboard_formats.txt", "w");
  371. #endif
  372.  
  373.     char format_name[MAX_PATH + 1]; // MSDN's RegisterClipboardFormat() doesn't document any max length, but the ones we're interested in certainly don't exceed MAX_PATH.
  374.     if (uFormat < 0xC000 || uFormat > 0xFFFF) // It's a registered format (you're supposted to verify in-range before calling GetClipboardFormatName()).  Also helps performance.
  375.         *format_name = '\0'; // Don't need the name if it's a standard/CF_* format.
  376.     else
  377.     {
  378.         // v1.0.42.04:
  379.         // Probably need to call GetClipboardFormatName() rather than comparing directly to uFormat because
  380.         // MSDN implies that OwnerLink and other registered formats might not always have the same ID under
  381.         // all OSes (past and future).
  382.         GetClipboardFormatName(uFormat, format_name, MAX_PATH);
  383.         // Since RegisterClipboardFormat() is case insensitive, the case might vary.  So use stricmp() when
  384.         // comparing format_name to anything.
  385.         // "Link Source", "Link Source Descriptor" , and anything else starting with "Link Source" is likely
  386.         // to be data that should not be attempted to be retrieved because:
  387.         // 1) It causes unwanted bookmark effects in various versions of MS Word.
  388.         // 2) Tests show that these formats are on the clipboard only if MS Word is open at the time
  389.         //    ClipboardAll is accessed.  That implies they're transitory formats that aren't as essential
  390.         //    or well suited to ClipboardAll as the other formats (but if it weren't for #1 above, this
  391.         //    wouldn't be enough reason to omit it).
  392.         // 3) Although there is hardly any documentation to be found at MSDN or elsewhere about these formats,
  393.         //    it seems they're related to OLE, with further implications that the data is transitory.
  394.         // Here are the formats that Word 2002 removes from the clipboard when it the app closes:
  395.         // 0xC002 ObjectLink  >>> Causes WORD bookmarking problem.
  396.         // 0xC003 OwnerLink
  397.         // 0xC00D Link Source  >>> Causes WORD bookmarking problem.
  398.         // 0xC00F Link Source Descriptor  >>> Doesn't directly cause bookmarking, but probably goes with above.
  399.         // 0xC0DC Hyperlink
  400.         if (   !strnicmp(format_name, "Link Source", 11) || !stricmp(format_name, "ObjectLink")
  401.             || !stricmp(format_name, "OwnerLink")
  402.             // v1.0.44.07: The following were added to solve interference with MS Outlook's MS Word editor.
  403.             // If a hotkey like ^F1::ClipboardSave:=ClipboardAll is pressed after pressing Ctrl-C in that
  404.             // editor (perhaps only when copying HTML), two of the following error dialogs would otherwise
  405.             // be displayed (this occurs in Outlook 2002 and probably later versions):
  406.             // "An outgoing call cannot be made since the application is dispatching an input-synchronous call."
  407.             || !stricmp(format_name, "Native") || !stricmp(format_name, "Embed Source")   )
  408.             return NULL;
  409.     }
  410.  
  411. #ifdef DEBUG_BY_LOGGING_CLIPBOARD_FORMATS
  412.     fprintf(fp, "%04X\t%s\n", uFormat, format_name);  // Since fclose() is never called, the program has to exit to close/release the file.
  413. #endif
  414.  
  415.     HANDLE h;
  416.     for (DWORD start_time = GetTickCount();;)
  417.     {
  418.         // Known failure conditions:
  419.         // GetClipboardData() apparently fails when the text on the clipboard is greater than a certain size
  420.         // (Even though GetLastError() reports "Operation completed successfully").  The data size at which
  421.         // this occurs is somewhere between 20 to 96 MB (perhaps depending on system's memory and CPU speed).
  422.         if (h = GetClipboardData(uFormat)) // Assign
  423.             return h;
  424.  
  425.         // It failed, so act according to the type of format and the timeout that's in effect.
  426.         // Certain standard (numerically constant) clipboard formats are known to validly yield NULL from a
  427.         // call to GetClipboardData().  Never retry these because it would only cause unnecessary delays
  428.         // (i.e. a failure until timeout).
  429.         // v1.0.42.04: More importantly, retrying them appears to cause problems with saving a Word/Excel
  430.         // clipboard via ClipboardAll.
  431.         if (uFormat == CF_HDROP // This format can fail "normally" for the reasons described at "clipboard_contains_files".
  432.             || !stricmp(format_name, "OwnerLink")) // Known to validly yield NULL from a call to GetClipboardData(), so don't retry it to avoid having to wait the full timeout period.
  433.             return NULL;
  434.  
  435.         if (g_ClipboardTimeout != -1) // We were not told to wait indefinitely and...
  436.             if (!g_ClipboardTimeout   // ...we were told to make only one attempt, or ...
  437.                 || (int)(g_ClipboardTimeout - (GetTickCount() - start_time)) <= SLEEP_INTERVAL_HALF) //...it timed out.
  438.                 // Above must cast to int or any negative result will be lost due to DWORD type.
  439.                 return NULL;
  440.  
  441.         // Use SLEEP_WITHOUT_INTERRUPTION to prevent MainWindowProc() from accepting new hotkeys
  442.         // during our operation, since a new hotkey subroutine might interfere with
  443.         // what we're doing here (e.g. if it tries to use the clipboard, or perhaps overwrites
  444.         // the deref buffer if this object's caller gave it any pointers into that memory area):
  445.         SLEEP_WITHOUT_INTERRUPTION(INTERVAL_UNSPECIFIED)
  446.     }
  447. }
  448.  
  449.  
  450.  
  451. ResultType Clipboard::Open()
  452. {
  453.     if (mIsOpen)
  454.         return OK;
  455.     for (DWORD start_time = GetTickCount();;)
  456.     {
  457.         if (OpenClipboard(g_hWnd))
  458.         {
  459.             mIsOpen = true;
  460.             return OK;
  461.         }
  462.         if (g_ClipboardTimeout != -1) // We were not told to wait indefinitely...
  463.             if (!g_ClipboardTimeout   // ...and we were told to make only one attempt, or ...
  464.                 || (int)(g_ClipboardTimeout - (GetTickCount() - start_time)) <= SLEEP_INTERVAL_HALF) //...it timed out.
  465.                 // Above must cast to int or any negative result will be lost due to DWORD type.
  466.                 return FAIL;
  467.         // Use SLEEP_WITHOUT_INTERRUPTION to prevent MainWindowProc() from accepting new hotkeys
  468.         // during our operation, since a new hotkey subroutine might interfere with
  469.         // what we're doing here (e.g. if it tries to use the clipboard, or perhaps overwrites
  470.         // the deref buffer if this object's caller gave it any pointers into that memory area):
  471.         SLEEP_WITHOUT_INTERRUPTION(INTERVAL_UNSPECIFIED)
  472.     }
  473. }
  474.